//
//  CiotManager.swift
//  ciotTest
//
//  Created by casdon on 2025/5/12.
//

import Combine
import ObjectiveC
import CoreBluetooth
import Network

class CiotManager: NSObject, ObservableObject
{
    @Published var wifiSSID:String = ""
    @Published var wifiPass:String = ""
    
    @Published var isVerified: Bool = false
    @Published var isWifiConnected: Bool = false
    @Published var isConnectingCloud: Bool = false
    @Published var isCompleted: Bool = false
    @Published var bleState: BLEProvisioningState = .idle
    @Published var foundPeripherals: [Peripheral] = []
    @Published var connectedDeviceId: String = "InvalidDevice"
    @Published var isSearching: Bool = false
    @Published var currentRequestType: Ciot_clink_request_type = Ciot_Clink_Request_AP
    
    var apSSID: String = ""
    var apPass: String = ""
    
    
    static let shared = CiotManager()
    private override init() {
        super.init()
        apProvisioner = CiotAPProvisioner()
        bleProvisioner = CiotBLEProvisioner(ssid: wifiPass, pass: wifiSSID)
        
        bleProvisioner.delegate = self
        apProvisioner.delegate = self 
        initializeLibCiot()
    }
    
    let pid = "CIOT-SDK"
    let did = "CIOT-iOS-SDK"
    let license = "b893c0f734c548e9068680bb3b1ade70e9eb263d3a3828524613e3590e602fa7000174114667255243a5a6312296000136873bf7e4de459bb167818a558637be93ad447f6a0addab"
    let appID:String = "TestAPPID"
    let userID:String = "TestUserID"
    let deviceID:String = "TestDeviceID"
    var bleProvisioner :CiotBLEProvisioner!
    var apProvisioner: CiotAPProvisioner!
    
    var ciot_mobile_adapter_manager: UnsafePointer<ciot_mobile_adapter_manager> {
        return ciot_mobile_adapter_manager_standalone()
    }
    
    
    private func initializeLibCiot() {
        ciot_init(pid, did, license)
        ciot_mobile_adapter_manager_init()
        ciot_mobile_adapter_manager_reg(
            c_mobile_get_app_deviceID,
            c_mobile_get_app_userID,
            c_mobile_ble_scan_device,
            c_mobile_ble_connect,
            c_mobile_ble_set_mtu_request,
            c_mobile_ble_set_rw_char,
            c_mobile_ble_message_send,
            c_mobile_ap_scan_device,
            c_mobile_ap_connect,
            c_mobile_ap_discovered_notify,
            c_mobile_ap_udp_client_connect,
            c_mobile_ap_tcp_client_connect,
            c_mobile_ap_tcp_message_send,
            c_mobile_get_wifi_ssid,
            c_mobile_get_wifi_pass,
            c_mobile_ble_discovered_notify,
            c_mobile_device_id_received,
            c_mobile_clink_event_notify
        )
    }
        
    // 开始搜索设备
    func startDiscover( milliseconds: Int32 ) -> Ciot_res {
        return ciot_mobile_adapter_manager.pointee.start_discover(milliseconds)
    }
    
    // 开始连接
    func startConnect(uid: String, type: Ciot_clink_request_type) -> Ciot_res
    {
        return ciot_mobile_adapter_manager.pointee.clink_request(uid, nil, type)
    }
    
    fileprivate func notifyDeviceDiscovered( uid: UnsafePointer<Int8>? , mac: UnsafePointer<Int8>?, pid: UnsafePointer<Int8>?, clinkStatus:Ciot_clink_connection_status ) -> Ciot_res {
        
        let swift_pid = String(cString: pid!)
        let swift_uid = String(cString: uid!)
        print("[Ciot DEBUG Mobile] notifyDeviceDiscovered \(swift_uid) \(String(describing: mac)) \(swift_pid) \(clinkStatus)")
        
        return Ciot_OK
    }
    
    fileprivate func notifyReceivedDeviceID( deviceId: UnsafePointer<Int8>? ) -> Ciot_res {
        let deviceID = String(cString: deviceId!)
        connectedDeviceId = deviceID
        print("[Ciot DEBUG Mobile] Received Device ID \(deviceID)")
        return Ciot_OK
    }
    
    fileprivate func notifyClinkEvent( event: UnsafePointer<Int8>?, message:UnsafePointer<Int8>? ) -> Ciot_res {
        
        let event_str = String(cString: event!)
        let message_str = message != nil ? String(cString: message!) : ""
        print("[Ciot DEBUG Mobile] notifyClinkEvent event_str = \(event_str), message_str = \(message_str)")
        
        switch event_str {
        case "device_verified":
            isVerified = true
            bleProvisioner.state = .deviceVerified
            break
        case "wifi_timeout", "wifi_pass_err":
            wifiPass = ""
            wifiSSID = ""
            break;
        case "wifi_connecting":
            isWifiConnected = false
            bleProvisioner.state = .wifiConnecting
            break
        case "wifi_connected":
            isWifiConnected = true
            isConnectingCloud = true
            bleProvisioner.state = .wifiConnected
            isCompleted = false
            let wifiInfo = WIFIStoreTools.WifiInfo.init(ssid: self.wifiSSID, password: self.wifiPass)
            let _ = WIFIStoreTools.shared.saveWifiInfo(wifiInfo)
            break
        case "succeed":
            isConnectingCloud = true
            isCompleted = true
        case "timeout":
            print("超时")
            break
        case "cloud_connecting":
            print("设备入云中")
            break
         case  "cloud_connected":
            print("设备入云成功")
            break
        default:
            break
        }
        print("[Ciot DEBUG Mobile] notifyClinkEvent \(event_str) \(message_str)")
        return Ciot_OK
    }
}

extension CiotManager: CiotManagerDelegate {

    func didDiscoverPeripherals(_ peripherals: [Peripheral], provisioner: CiotBLEProvisioner)
    {
        CiotManager.shared.foundPeripherals = peripherals
        print(#line, "[Ciot DEBUG Mobile] didDiscoverPeripherals \(peripherals.count)")
    }
    
    func updateState(_ state: BLEProvisioningState, provisioner: CiotBLEProvisioner)
    {
        bleState = state
        print(#line, "[Ciot DEBUG Mobile] updateState \(state)")
    }
    
    func scanResult(uuid: String, rspData: Data, ssid: String, encrypted: Bool) -> Ciot_res
    {
        if currentRequestType == Ciot_Clink_Request_BLE
        {
            let cStringMutable: UnsafeMutablePointer<CChar>? = strdup(uuid)
            let uuid: UnsafePointer<CChar>? = cStringMutable.map { UnsafePointer($0) }
            let mac:UnsafePointer<UInt8>? = nil
            
            if rspData.count < 5 { return Ciot_Err }
            
           let _ = rspData.withUnsafeBytes { (rawBuffer: UnsafeRawBufferPointer) in
                let crspData:UnsafePointer<UInt8> = rawBuffer.bindMemory(to: UInt8.self).baseAddress!
                let length: UInt8 = UInt8(rspData.count) + 1;
                let prefix: [UInt8] = [length, 255]
                let body = Array(UnsafeBufferPointer(start: crspData, count: rspData.count))
                let newRspData: [UInt8] = prefix + body
               return ciot_mobile_adapter_manager.pointee.ble_scan_result_handler( uuid, mac, newRspData , rspData.count)
            }
            
            // 使用完后释放
            if let cStringMutable = cStringMutable {
                free(cStringMutable)
            }
            return Ciot_Err

        }
        
        if currentRequestType == Ciot_Clink_Request_AP
        {
            let cssid  = convertToCChar(str: ssid)
            return ciot_mobile_adapter_manager.pointee.ap_scan_result_handler( cssid, encrypted )
        }
        return Ciot_Err
    }
    
    func connected(uuid: String, ssid: String, pass: String)  -> Ciot_res
    {
        let uuid: UnsafePointer<Int8>? = convertToCChar(str: uuid)
        let mac: UnsafePointer<UInt8>? = nil
        if currentRequestType ==  Ciot_Clink_Request_BLE
        {
            return ciot_mobile_adapter_manager.pointee.ble_connected(uuid, mac)
        }
        
        if currentRequestType == Ciot_Clink_Request_AP
        {
            return ciot_mobile_adapter_manager.pointee.ap_connected(apSSID, apPass)
        }
        
        return Ciot_Err
    }
    
    func connect(uuid: String, ssid: String, pass: String) -> Ciot_res {
        
        let uuid: UnsafePointer<Int8>? = convertToCChar(str: uuid)
        let mac: UnsafePointer<UInt8>? = nil
        
        if currentRequestType ==  Ciot_Clink_Request_BLE
        {
            return ciot_mobile_adapter_manager.pointee.ble_connect(uuid, mac)
        }
        
        if currentRequestType == Ciot_Clink_Request_AP
        {
            return ciot_mobile_adapter_manager.pointee.ap_connect(apSSID, apPass)
        }
        
        return Ciot_Err
    }
    
    func disConnected(uuid: String, ssid: String, pass: String) -> Ciot_res {
        let uuid: UnsafePointer<Int8>? = convertToCChar(str: uuid)
        let mac: UnsafePointer<UInt8>? = nil
        
        if currentRequestType ==  Ciot_Clink_Request_BLE
        {
            return ciot_mobile_adapter_manager.pointee.ble_disconnected(uuid, mac)
        }
        
        if currentRequestType == Ciot_Clink_Request_AP
        {
            return ciot_mobile_adapter_manager.pointee.ap_disconnected(apSSID, apPass)
        }
        return Ciot_OK
    }
    
    func wr_ready() -> Ciot_res
    {
        ciot_mobile_adapter_manager.pointee.ble_wr_ready()
    }
    
    func message_received(characteristicValue: Data) -> Ciot_res
    {
        let ble_message: [UInt8] = characteristicValue.map { $0 }
        return ciot_mobile_adapter_manager.pointee.ble_message_received(ble_message, characteristicValue.count)
    }
}

// MARK: - C function wrappers
    
func c_mobile_get_app_deviceID()->UnsafePointer<Int8>? {

    return convertToCChar(str: "TestAPPID")
}

func c_mobile_get_app_userID()->UnsafePointer<Int8>? {
    return convertToCChar(str: "TestUserID")
}

func c_mobile_ble_scan_device(_name: UnsafePointer<Int8>?, _uuid: UnsafePointer<Int8>?) -> Ciot_res {
    return CiotManager.shared.bleProvisioner.c_mobile_ble_scan_device(name: _name, uuid: _uuid)
}

func c_mobile_ble_connect(_uid: UnsafePointer<Int8>?, _mac: UnsafePointer<Int8>?) -> Ciot_res {
    return CiotManager.shared.bleProvisioner.c_mobile_ble_connect(uid: _uid, mac: _mac)
}

func c_mobile_ble_set_mtu_request(_mtu: Int32) -> Ciot_res {
    return CiotManager.shared.bleProvisioner.c_mobile_ble_set_mtu_request(mtu: _mtu)
}

func c_mobile_ble_set_rw_char(_readChar: UnsafePointer<Int8>?, _writeChar: UnsafePointer<Int8>?) -> Ciot_res {
    return CiotManager.shared.bleProvisioner.c_mobile_ble_set_rw_char(readChar: _readChar, writeChar: _writeChar)
}

func c_mobile_ble_message_send(_data: UnsafePointer<UInt8>?, _len: Int) -> Ciot_res {
    return CiotManager.shared.bleProvisioner.c_mobile_ble_message_send(data: _data, len: _len)
}


func c_mobile_ap_scan_device(_name: UnsafePointer<Int8>?) -> Ciot_res {
    /// TODO:
    print("[Ciot DEBUG Mobile] c_mobile_ap_scan_device\(_name) ")
    
    return CiotManager.shared.apProvisioner.c_mobile_ap_scan_device(name: _name)
}

func c_mobile_ap_connect(_ssid: UnsafePointer<Int8>?, _pass: UnsafePointer<Int8>?) -> Ciot_res {
    /// TODO:
    var strssid = "", strpass = ""
    if let ssid = _ssid
    {
        strssid = String(cString: ssid)
    }
    if let pass = _pass
    {
        strpass = String(cString: pass)
    }
    print("[Ciot DEBUG Mobile] c_mobile_ap_connect ssid = \(_ssid), pass = \(_pass) ")
    return CiotManager.shared.apProvisioner.c_mobile_ap_connect(ssid: strssid, pass: strpass)
}

func c_mobile_ap_discovered_notify(_name: UnsafePointer<Int8>?, _pid: UnsafePointer<Int8>?, clinkStatus: Ciot_clink_connection_status, encrypted: Bool, password: UnsafePointer<Int8>?, random: Int32) -> Ciot_res {
    /// TODO:
    var strname = "", strpid = "", strPwd = ""
    if let name = _name
    {
        strname = String(cString: name)
    }
    if let pid = _pid
    {
        strpid = String(cString: pid)
    }
    if let password = password
    {
        strPwd = String(cString: password)
    }

    print("[Ciot DEBUG Mobile] c_mobile_ap_discovered_notify _name = \(strname), _pid = \(strpid) , clinkStatus = \(clinkStatus), password:\(strPwd), random: \(random)")
    return CiotManager.shared.apProvisioner.c_mobile_ap_discovered_notify(name: strname, pid: strpid, clinkStatus: clinkStatus, encrypted: encrypted, password: strPwd, random: random)
}

func c_mobile_ap_udp_client_connect(_port:Int32 ) -> Ciot_res
{
    print("[Ciot DEBUG Mobile] c_mobile_ap_udp_client_connect _port = \(_port)")
    return CiotManager.shared.apProvisioner.c_mobile_ap_udp_client_connect(port: _port)
}

func c_mobile_ap_tcp_client_connect(_ip: UnsafePointer<Int8>?, _port: Int32) -> Ciot_res
{
    var strip = ""
    if let ip = _ip
    {
        strip = String(cString: ip)
    }
    print("[Ciot DEBUG Mobile] c_mobile_ap_tcp_client_connect _port = \(strip), _ip = \(_ip)")
    return CiotManager.shared.apProvisioner.c_mobile_ap_tcp_client_connect(ip: strip, port: _port)
}

func c_mobile_ap_tcp_message_send(_data: UnsafePointer<UInt8>?, _len: Int) -> Ciot_res
{
    // TODO:
    print("[Ciot DEBUG Mobile] c_mobile_ap_tcp_message_send _data = \(_data), _len = \(_len)")
    return CiotManager.shared.apProvisioner.c_mobile_ap_tcp_message_send(data: _data, len: _len)
}


func c_mobile_get_wifi_ssid()->UnsafePointer<Int8>? {
    let manager = CiotManager.shared
    manager.bleProvisioner.state = .sendingCredentials
    let ssid = convertToCChar(str:CiotManager.shared.wifiSSID)
    print("[Ciot DEBUG Mobile] ciot sdk 获取 wifi ssid \(CiotManager.shared.wifiSSID)")
    return ssid
}


func c_mobile_get_wifi_pass()->UnsafePointer<Int8>? {
    let manager = CiotManager.shared
    manager.bleProvisioner.state = .sendingCredentials
    let pwd = convertToCChar(str:CiotManager.shared.wifiPass)
    print("[Ciot DEBUG Mobile] ciot sdk 获取 wifi pwd \(CiotManager.shared.wifiPass)")
    return pwd
}

func c_mobile_ble_discovered_notify(_uid: UnsafePointer<Int8>?, _mac: UnsafePointer<Int8>?, _pid: UnsafePointer<Int8>?, _clinkStatus: Ciot_clink_connection_status, _random: Int32) -> Ciot_res {
    let manager = CiotManager.shared
    return manager.bleProvisioner.c_mobile_ble_discovered_notify(uid: _uid, mac: _mac, pid: _pid, clinkStatus: _clinkStatus, random: _random)
}

func c_mobile_device_id_received(_deviceId: UnsafePointer<Int8>?) -> Ciot_res {
    let manager = CiotManager.shared
    return manager.notifyReceivedDeviceID(deviceId: _deviceId)
}

func c_mobile_clink_event_notify(_enum: Ciot_clink_connection_status, _event: UnsafePointer<Int8>?, _message:UnsafePointer<Int8>?) -> Ciot_res {
    let manager = CiotManager.shared
    return manager.notifyClinkEvent(event: _event, message: _message)
}
